Simon Elsässer, Karolinska Institutet (2023)

DESeq2 analysis of peak universe (joint peak set called in any condition, must be called in all three replicates, MACS3)

bw_dir <- "/Volumes/DATA/DATA/Puck/bigwig/"
chromhmm <- "../genome/ESC_10_segments.mm39.bed"

library("wigglescout")
library("ggpubr")
library("ggplot2")
library("DESeq2")
library("dplyr")
library("ggrastr")

clean <- function (fn) {
  fn <- gsub(pattern = ".+/", "", x = fn)
  fn <- gsub(pattern = ".mm9.+", "", x = fn)
  fn <- gsub(pattern = ".mm39.+", "", x = fn)
  fn <- gsub(pattern = "_S.+", "", x = fn)
  fn <- gsub(pattern = ".scaled.bw", "", x = fn)
  fn <- gsub(pattern = ".unscaled.bw", "", x = fn)
  fn <- gsub(pattern = "_batch2", "", x = fn)
  fn <- gsub(pattern = "-", " ", x = fn)
  fn <- gsub(pattern = "_", " ", x = fn)
  fn <- gsub(pattern = " HA ", " ", x = fn)
  fn <- gsub(pattern = "D1D6", "FANCJ-/-", x = fn)
  fn <- gsub(pattern = "P2D2", "DHX36-/-", x = fn)
  fn <- gsub(pattern = "P3D4", "FANCJ-/-DHX36-/-", x = fn)
  return(fn)
}

BWs <- paste0(bw_dir,list.files(bw_dir,pattern="G4_.+R.\\.bw"))

mypal <-c("cornflowerblue","orange","red2","#505050")
mypal2 <- rep(mypal,each=2)
mypal3 <-c("cornflowerblue","cornflowerblue","cornflowerblue","orange","orange","orange","red2","red2","red2","505050","505050","505050")
bw_granges_diff_analysis <- function(granges_c1,
                                     granges_c2,
                                     label_c1,
                                     label_c2,
                                     estimate_size_factors = FALSE,
                                     as_granges = FALSE) {

  # Bind first, get numbers after
  names_values <- NULL
  fields <- names(mcols(granges_c1))

  if ("name" %in% fields) {
    names_values <- mcols(granges_c1)[["name"]]
    granges_c1 <- granges_c1[, fields[fields != "name"]]
  }

  fields <- names(mcols(granges_c2))
  if ("name" %in% fields) {
    granges_c2 <- granges_c2[, fields[fields != "name"]]
  }

  cts_df <- cbind(data.frame(granges_c1), mcols(granges_c2))

  if (! is.null(names_values)) {
    rownames(cts_df) <- names_values
  }

  # Needs to drop non-complete cases and match rows
  complete <- complete.cases(cts_df)
  cts_df <- cts_df[complete, ]

  values_df <- cts_df[, 6:ncol(cts_df)] %>% dplyr::select(where(is.numeric))
  cts <- get_nreads_columns(values_df)

  condition_labels <- c(rep(label_c1, length(mcols(granges_c1))),
                        rep(label_c2, length(mcols(granges_c2))))


  coldata <- data.frame(colnames(cts), condition = as.factor(condition_labels))

  dds <- DESeq2::DESeqDataSetFromMatrix(countData = cts,
                                colData = coldata,
                                design = ~ condition,
                                rowRanges = granges_c1[complete, ])


  if (estimate_size_factors == TRUE) {
    dds <- DESeq2::estimateSizeFactors(dds)
  }
  else {
    # Since files are scaled, we do not want to estimate size factors
    sizeFactors(dds) <- c(rep(1, ncol(cts)))
  }

  dds <- DESeq2::estimateDispersions(dds)
  dds <- DESeq2::nbinomWaldTest(dds)

  if (as_granges) {
    result <- DESeq2::results(dds, format = "GRanges",alpha = 0.01)
    if (!is.null(names_values)) {
      result$name <- names_values[complete]
    }

  }
  else {
    # result <- results(dds, format="DataFrame")
    result <- dds
  }

  result
}

get_nreads_columns <- function(df, length_factor = 100) {
  # Convert mean coverages to round integer read numbers
  cts <- as.matrix(df)
  cts <- as.matrix(cts[complete.cases(cts),])
  cts <- round(cts*length_factor)
  cts
}

BWs_Rloop <- paste0(bw_dir,list.files(bw_dir,pattern="Rloop_.+_R..bw"))
gencode <- "../genome/gencode.vm33.mm39.bed"
gencode <- "../genome/genes_lo_mid_hi_bi.mm39.bed"

BWs.WT <- BWs_Rloop[grep("WT",BWs_Rloop)]
BWs.FANCJ <- BWs_Rloop[grep("D1D6",BWs_Rloop)]
BWs.DHX36 <- BWs_Rloop[grep("P2D2",BWs_Rloop)]
BWs.DKO <- BWs_Rloop[grep("P3D4",BWs_Rloop)]
  
# Calculate here some loci or bins
cov.WT <- bw_loci(BWs.WT, loci = gencode)
cov.DHX36 <- bw_loci(BWs.DHX36, loci = gencode)
cov.FANCJ <- bw_loci(BWs.FANCJ, loci = gencode)
cov.DKO <- bw_loci(BWs.DKO, loci = gencode)

cov.WT$name <- paste0("peak_",1:length(cov.WT))
cov.DHX36$name <- paste0("peak_",1:length(cov.DHX36))
cov.FANCJ$name <- paste0("peak_",1:length(cov.FANCJ))
cov.DKO$name <- paste0("peak_",1:length(cov.DKO))
diff_DHX36 <- bw_granges_diff_analysis(cov.WT, cov.DHX36,
                                     "WT", "DHX36KO")
converting counts to integer mode
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
diff_FANCJ <- bw_granges_diff_analysis(cov.WT, cov.FANCJ,
                                     "WT", "FANCJ")
converting counts to integer mode
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
diff_DKO <- bw_granges_diff_analysis(cov.WT, cov.DKO,
                                     "WT", "DKO")
converting counts to integer mode
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
# This takes care of low conts and things like this, but you can also use
# diff_results as is for the things below
lfc_DHX36 <- DESeq2::lfcShrink(diff_DHX36, coef = "condition_WT_vs_DHX36KO", type="apeglm")
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
lfc_FANCJ <- DESeq2::lfcShrink(diff_FANCJ, coef = "condition_WT_vs_FANCJ", type="apeglm")
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
lfc_DKO <- DESeq2::lfcShrink(diff_DKO, coef = "condition_WT_vs_DKO", type="apeglm")
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
data_DHX36 <- plotMA(lfc_DHX36, returnData = T)
data_DHX36$lfc <- -data_DHX36$lfc
data_DHX36$mean <- log10(data_DHX36$mean)

data_FANCJ <- plotMA(lfc_FANCJ, returnData = T)
data_FANCJ$lfc <- -data_FANCJ$lfc
data_FANCJ$mean <- log10(data_FANCJ$mean)

data_DKO <- plotMA(lfc_DKO, returnData = T)
data_DKO$lfc <- -data_DKO$lfc
data_DKO$mean <- log10(data_DKO$mean)
ggscatter(data_DHX36,x ="mean",y="lfc",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[1])) + geom_hline(yintercept = 0, linetype="dashed", size=0.1) + coord_cartesian(ylim=c(-5,5))

plot_MA_DHX36 <- rasterize(last_plot(), layers='Point', dpi=600) 
data_DHX36$cov.WT <- rowMeans(as.data.frame(cov.WT)[,6:7])
data_DHX36$cov.DHX36 <- rowMeans(as.data.frame(cov.DHX36)[,6:7])
ggscatter(data_DHX36,x ="cov.WT",y="cov.DHX36",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[1])) + scale_x_continuous(trans="log",limits = c(0.5,400)) + scale_y_continuous(trans="log",limits = c(0.5,400)) + geom_abline(slope = 1, linetype="dashed", size=0.1)

plot_XY_DHX36 <- rasterize(last_plot(), layers='Point', dpi=600) 
data_DHX36$cov.WT <- rowMeans(as.data.frame(cov.WT)[,6:7])
data_DHX36$cov.DHX36 <- rowMeans(as.data.frame(cov.DHX36)[,6:7])
ggscatter(data_DHX36,x ="cov.WT",y="cov.DHX36",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[1])) + scale_x_continuous(limits = c(-10,50)) + scale_y_continuous(limits = c(-10,50)) + geom_abline(slope = 1, linetype="dashed", size=0.1)

plot_XY_DHX36_zoom <- rasterize(last_plot(), layers='Point', dpi=600) 
ggscatter(data_FANCJ,x ="mean",y="lfc",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[2])) + geom_hline(yintercept = 0, linetype="dashed", size=0.1) + coord_cartesian(ylim=c(-5,5))

plot_MA_FANCJ <- rasterize(last_plot(), layers='Point', dpi=600) 
data_FANCJ$cov.WT <- rowMeans(as.data.frame(cov.WT)[,6:7])
data_FANCJ$cov.FANCJ <- rowMeans(as.data.frame(cov.FANCJ)[,6:7])
ggscatter(data_FANCJ,x ="cov.WT",y="cov.FANCJ",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[2])) + geom_hline(yintercept = 0, linetype="dashed", size=0.1) + scale_x_continuous(trans="log",limits = c(0.5,400)) + scale_y_continuous(trans="log",limits = c(0.5,400)) + geom_abline(slope = 1, linetype="dashed", size=0.1)

plot_XY_FANCJ <- rasterize(last_plot(), layers='Point', dpi=600) 
ggscatter(data_DKO,x ="mean",y="lfc",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[3])) + geom_hline(yintercept = 0, linetype="dashed", size=0.1) + coord_cartesian(ylim=c(-5,5))

plot_MA_DKO <- rasterize(last_plot(), layers='Point', dpi=600) 
data_DKO$cov.WT <- rowMeans(as.data.frame(cov.WT)[,6:7])
data_DKO$cov.DKO <- rowMeans(as.data.frame(cov.DKO)[,6:7])
ggscatter(data_DKO,x ="cov.WT",y="cov.DKO",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[3])) + geom_hline(yintercept = 0, linetype="dashed", size=0.1) + scale_x_continuous(trans="log",limits = c(0.5,400)) + scale_y_continuous(trans="log",limits = c(0.5,400)) + geom_abline(slope = 1, linetype="dashed", size=0.1)

plot_XY_DKO <- rasterize(last_plot(), layers='Point', dpi=600) 
data_DKO$lfc_DHX36 <- data_DHX36$lfc
data_DKO$lfc_FANCJ <- data_FANCJ$lfc
ggscatter(data_DKO,x ="lfc_DHX36",y="lfc",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[3]))+ geom_hline(yintercept = 0, linetype="dashed", size=0.1) + geom_vline(xintercept = 0, linetype="dashed", size=0.1) + coord_cartesian(ylim=c(-5,5), xlim=c(-5,5))

plot_LFC_DKO_vs_DHX36 <- rasterize(last_plot(), layers='Point', dpi=600)
ggscatter(data_DKO,x ="lfc_FANCJ",y="lfc",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[3]))+ geom_hline(yintercept = 0, linetype="dashed", size=0.1) + geom_vline(xintercept = 0, linetype="dashed", size=0.1) + coord_cartesian(ylim=c(-5,5), xlim=c(-5,5))

plot_LFC_DKO_vs_FANCJ <- rasterize(last_plot(), layers='Point', dpi=600)
data_DHX36$lfc_DKO <- data_DKO$lfc
ggscatter(data_DHX36,x ="lfc",y="lfc_DKO",color="isDE",size = 0.8, alpha=0.5, palette = c("gray",mypal[1]))+ geom_hline(yintercept = 0, linetype="dashed", size=0.1) + geom_vline(xintercept = 0, linetype="dashed", size=0.1) + coord_cartesian(ylim=c(-5,5), xlim=c(-5,5))

cov <- cbind( as.data.frame(cov.WT)[,1:7], 
              as.data.frame(cov.DHX36)[,6:7], 
              as.data.frame(cov.FANCJ)[,6:7], 
              as.data.frame(cov.DKO)[,6:7])

colnames(cov) <- c(colnames(cov)[1:5],"WT1","WT2","DHX1","DHX2","FAN1","FAN2","DKO1","DKO2")
library(reshape2)
mdf <- melt(data.frame(name=cov.WT$name,cov[,6:13]))
Using name as id variables
mdf <- mdf[mdf$value<500,]
mdf$cond <- "WT"
mdf$cond[grep("DHX",mdf$variable)] <- "DHX36-/-"
mdf$cond[grep("FAN",mdf$variable)] <- "FANCJ-/-"
mdf$cond[grep("DKO",mdf$variable)] <- "DHX36-/-FANCJ-/-"
plot_viol_rep_all_peaks <- ggviolin(mdf, x="variable",y="value",fill="cond",palette = mypal, add="mean_sd") +
  coord_cartesian(ylim=c(0,10))
plot_viol_rep_all_peaks

plot_viol_rep_hi_genes <- ggviolin(mdf[mdf$name=="gene_hi",], x="variable",y="value",fill="cond",palette = mypal, add="mean_sd") +
  coord_cartesian(ylim=c(0,10))
plot_viol_rep_hi_genes

plot_viol_rep_biv_genes <- ggviolin(mdf[mdf$name=="gene_bi",], x="variable",y="value",fill="cond",palette = mypal, add="mean_sd") +
  coord_cartesian(ylim=c(0,10))
plot_viol_rep_biv_genes

plot_viol_rep_by_genes <- ggviolin(mdf, x="name",y="value",fill="cond",palette = mypal, add="mean_sd") +
  coord_cartesian(ylim=c(0,10))
plot_viol_rep_by_genes

LS0tCnRpdGxlOiAiRzQgQ1VUJlRhZyBhbmFseXNpcyBtRVNDIChXVCwgRkFOQ0ogS08sIERIWDM2IEtPLCBES08pIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpTaW1vbiBFbHPDpHNzZXIsIEthcm9saW5za2EgSW5zdGl0dXRldCAoMjAyMykKCiMjIyBERVNlcTIgYW5hbHlzaXMgb2YgcGVhayB1bml2ZXJzZSAoam9pbnQgcGVhayBzZXQgY2FsbGVkIGluIGFueSBjb25kaXRpb24sIG11c3QgYmUgY2FsbGVkIGluIGFsbCB0aHJlZSByZXBsaWNhdGVzLCBNQUNTMykKCgpgYGB7ciBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zfQpid19kaXIgPC0gIi9Wb2x1bWVzL0RBVEEvREFUQS9QdWNrL2JpZ3dpZy8iCmNocm9taG1tIDwtICIuLi9nZW5vbWUvRVNDXzEwX3NlZ21lbnRzLm1tMzkuYmVkIgoKbGlicmFyeSgid2lnZ2xlc2NvdXQiKQpsaWJyYXJ5KCJnZ3B1YnIiKQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgiREVTZXEyIikKbGlicmFyeSgiZHBseXIiKQpsaWJyYXJ5KCJnZ3Jhc3RyIikKCmNsZWFuIDwtIGZ1bmN0aW9uIChmbikgewogIGZuIDwtIGdzdWIocGF0dGVybiA9ICIuKy8iLCAiIiwgeCA9IGZuKQogIGZuIDwtIGdzdWIocGF0dGVybiA9ICIubW05LisiLCAiIiwgeCA9IGZuKQogIGZuIDwtIGdzdWIocGF0dGVybiA9ICIubW0zOS4rIiwgIiIsIHggPSBmbikKICBmbiA8LSBnc3ViKHBhdHRlcm4gPSAiX1MuKyIsICIiLCB4ID0gZm4pCiAgZm4gPC0gZ3N1YihwYXR0ZXJuID0gIi5zY2FsZWQuYnciLCAiIiwgeCA9IGZuKQogIGZuIDwtIGdzdWIocGF0dGVybiA9ICIudW5zY2FsZWQuYnciLCAiIiwgeCA9IGZuKQogIGZuIDwtIGdzdWIocGF0dGVybiA9ICJfYmF0Y2gyIiwgIiIsIHggPSBmbikKICBmbiA8LSBnc3ViKHBhdHRlcm4gPSAiLSIsICIgIiwgeCA9IGZuKQogIGZuIDwtIGdzdWIocGF0dGVybiA9ICJfIiwgIiAiLCB4ID0gZm4pCiAgZm4gPC0gZ3N1YihwYXR0ZXJuID0gIiBIQSAiLCAiICIsIHggPSBmbikKICBmbiA8LSBnc3ViKHBhdHRlcm4gPSAiRDFENiIsICJGQU5DSi0vLSIsIHggPSBmbikKICBmbiA8LSBnc3ViKHBhdHRlcm4gPSAiUDJEMiIsICJESFgzNi0vLSIsIHggPSBmbikKICBmbiA8LSBnc3ViKHBhdHRlcm4gPSAiUDNENCIsICJGQU5DSi0vLURIWDM2LS8tIiwgeCA9IGZuKQogIHJldHVybihmbikKfQoKQldzIDwtIHBhc3RlMChid19kaXIsbGlzdC5maWxlcyhid19kaXIscGF0dGVybj0iRzRfLitjb21iaW5lZC5idyIpKQoKbXlwYWwgPC1jKCJjb3JuZmxvd2VyYmx1ZSIsIm9yYW5nZSIsInJlZDIiLCIjNTA1MDUwIikKbXlwYWwyIDwtIHJlcChteXBhbCxlYWNoPTIpCm15cGFsMyA8LWMoImNvcm5mbG93ZXJibHVlIiwiY29ybmZsb3dlcmJsdWUiLCJjb3JuZmxvd2VyYmx1ZSIsIm9yYW5nZSIsIm9yYW5nZSIsIm9yYW5nZSIsInJlZDIiLCJyZWQyIiwicmVkMiIsIjUwNTA1MCIsIjUwNTA1MCIsIjUwNTA1MCIpCmBgYAoKYGBge3J9CmJ3X2dyYW5nZXNfZGlmZl9hbmFseXNpcyA8LSBmdW5jdGlvbihncmFuZ2VzX2MxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3Jhbmdlc19jMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsX2MxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxfYzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlc3RpbWF0ZV9zaXplX2ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzX2dyYW5nZXMgPSBGQUxTRSkgewoKICAjIEJpbmQgZmlyc3QsIGdldCBudW1iZXJzIGFmdGVyCiAgbmFtZXNfdmFsdWVzIDwtIE5VTEwKICBmaWVsZHMgPC0gbmFtZXMobWNvbHMoZ3Jhbmdlc19jMSkpCgogIGlmICgibmFtZSIgJWluJSBmaWVsZHMpIHsKICAgIG5hbWVzX3ZhbHVlcyA8LSBtY29scyhncmFuZ2VzX2MxKVtbIm5hbWUiXV0KICAgIGdyYW5nZXNfYzEgPC0gZ3Jhbmdlc19jMVssIGZpZWxkc1tmaWVsZHMgIT0gIm5hbWUiXV0KICB9CgogIGZpZWxkcyA8LSBuYW1lcyhtY29scyhncmFuZ2VzX2MyKSkKICBpZiAoIm5hbWUiICVpbiUgZmllbGRzKSB7CiAgICBncmFuZ2VzX2MyIDwtIGdyYW5nZXNfYzJbLCBmaWVsZHNbZmllbGRzICE9ICJuYW1lIl1dCiAgfQoKICBjdHNfZGYgPC0gY2JpbmQoZGF0YS5mcmFtZShncmFuZ2VzX2MxKSwgbWNvbHMoZ3Jhbmdlc19jMikpCgogIGlmICghIGlzLm51bGwobmFtZXNfdmFsdWVzKSkgewogICAgcm93bmFtZXMoY3RzX2RmKSA8LSBuYW1lc192YWx1ZXMKICB9CgogICMgTmVlZHMgdG8gZHJvcCBub24tY29tcGxldGUgY2FzZXMgYW5kIG1hdGNoIHJvd3MKICBjb21wbGV0ZSA8LSBjb21wbGV0ZS5jYXNlcyhjdHNfZGYpCiAgY3RzX2RmIDwtIGN0c19kZltjb21wbGV0ZSwgXQoKICB2YWx1ZXNfZGYgPC0gY3RzX2RmWywgNjpuY29sKGN0c19kZildICU+JSBkcGx5cjo6c2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKQogIGN0cyA8LSBnZXRfbnJlYWRzX2NvbHVtbnModmFsdWVzX2RmKQoKICBjb25kaXRpb25fbGFiZWxzIDwtIGMocmVwKGxhYmVsX2MxLCBsZW5ndGgobWNvbHMoZ3Jhbmdlc19jMSkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmVwKGxhYmVsX2MyLCBsZW5ndGgobWNvbHMoZ3Jhbmdlc19jMikpKSkKCgogIGNvbGRhdGEgPC0gZGF0YS5mcmFtZShjb2xuYW1lcyhjdHMpLCBjb25kaXRpb24gPSBhcy5mYWN0b3IoY29uZGl0aW9uX2xhYmVscykpCgogIGRkcyA8LSBERVNlcTI6OkRFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gY3RzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2xkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gY29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd1JhbmdlcyA9IGdyYW5nZXNfYzFbY29tcGxldGUsIF0pCgoKICBpZiAoZXN0aW1hdGVfc2l6ZV9mYWN0b3JzID09IFRSVUUpIHsKICAgIGRkcyA8LSBERVNlcTI6OmVzdGltYXRlU2l6ZUZhY3RvcnMoZGRzKQogIH0KICBlbHNlIHsKICAgICMgU2luY2UgZmlsZXMgYXJlIHNjYWxlZCwgd2UgZG8gbm90IHdhbnQgdG8gZXN0aW1hdGUgc2l6ZSBmYWN0b3JzCiAgICBzaXplRmFjdG9ycyhkZHMpIDwtIGMocmVwKDEsIG5jb2woY3RzKSkpCiAgfQoKICBkZHMgPC0gREVTZXEyOjplc3RpbWF0ZURpc3BlcnNpb25zKGRkcykKICBkZHMgPC0gREVTZXEyOjpuYmlub21XYWxkVGVzdChkZHMpCgogIGlmIChhc19ncmFuZ2VzKSB7CiAgICByZXN1bHQgPC0gREVTZXEyOjpyZXN1bHRzKGRkcywgZm9ybWF0ID0gIkdSYW5nZXMiLGFscGhhID0gMC4wMSkKICAgIGlmICghaXMubnVsbChuYW1lc192YWx1ZXMpKSB7CiAgICAgIHJlc3VsdCRuYW1lIDwtIG5hbWVzX3ZhbHVlc1tjb21wbGV0ZV0KICAgIH0KCiAgfQogIGVsc2UgewogICAgIyByZXN1bHQgPC0gcmVzdWx0cyhkZHMsIGZvcm1hdD0iRGF0YUZyYW1lIikKICAgIHJlc3VsdCA8LSBkZHMKICB9CgogIHJlc3VsdAp9CgpnZXRfbnJlYWRzX2NvbHVtbnMgPC0gZnVuY3Rpb24oZGYsIGxlbmd0aF9mYWN0b3IgPSAxMDApIHsKICAjIENvbnZlcnQgbWVhbiBjb3ZlcmFnZXMgdG8gcm91bmQgaW50ZWdlciByZWFkIG51bWJlcnMKICBjdHMgPC0gYXMubWF0cml4KGRmKQogIGN0cyA8LSBhcy5tYXRyaXgoY3RzW2NvbXBsZXRlLmNhc2VzKGN0cyksXSkKICBjdHMgPC0gcm91bmQoY3RzKmxlbmd0aF9mYWN0b3IpCiAgY3RzCn0KYGBgCgoKYGBge3IgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RixlY2hvPUYscHJvbXB0PUYsd2FybmluZz1GfQpid19kaXIgPC0gIi9Wb2x1bWVzL0RBVEEvREFUQS9QdWNrL2JpZ3dpZy8vIgpCV3NfUmxvb3AgPC0gcGFzdGUwKGJ3X2RpcixsaXN0LmZpbGVzKGJ3X2RpcixwYXR0ZXJuPSJSbG9vcF8uK19jb21iaW5lZC5idyIpKQpwbG90X3Byb2ZpbGVfYml2X2dlbmVzX1Jsb29wIDwtIHBsb3RfYndfcHJvZmlsZShid2ZpbGVzID0gQldzX1Jsb29wLGxvY2kgPSAiLi4vZ2Vub21lL0syN19iaXZhbGVudF9nZW5lcy5tbTM5LmJlZCIsIG1vZGU9InN0cmV0Y2giLCBsYWJlbHMgPSBjbGVhbihCV3NfUmxvb3ApLHNob3dfZXJyb3I9VCxjb2xvcnMgPSBteXBhbCwgdmVyYm9zZT1GLCByZW1vdmVfdG9wPTAuMDAxKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygwLDIwKSkKcGxvdF9wcm9maWxlX2Jpdl9nZW5lc19SbG9vcApgYGAKCmBgYHtyIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsZWNobz1GLHByb21wdD1GLHdhcm5pbmc9Rn0KYndfZGlyIDwtICIvVm9sdW1lcy9EQVRBL0RBVEEvUHVjay9iaWd3aWcvLyIKQldzX1Jsb29wIDwtIHBhc3RlMChid19kaXIsbGlzdC5maWxlcyhid19kaXIscGF0dGVybj0iUmxvb3BfLitfUi4uYnciKSkKcGxvdF9wcm9maWxlX2Jpdl9nZW5lc19SbG9vcF9ES08gPC0gcGxvdF9id19wcm9maWxlKGJ3ZmlsZXMgPSBCV3NfUmxvb3AsbG9jaSA9ICIuLi9nZW5vbWUvSzI3X2JpdmFsZW50X2dlbmVzLm1tMzkuYmVkIiwgbW9kZT0ic3RyZXRjaCIsIGxhYmVscyA9IGNsZWFuKEJXc19SbG9vcCksc2hvd19lcnJvcj1ULGNvbG9ycyA9IG15cGFsMiwgdmVyYm9zZT1GLCByZW1vdmVfdG9wPTAuMDAxKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygwLDIwKSkKcGxvdF9wcm9maWxlX2Jpdl9nZW5lc19SbG9vcF9ES08gCmBgYAoKYGBge3IgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RixlY2hvPUYscHJvbXB0PUYsd2FybmluZz1GfQpid19kaXIgPC0gIi9Wb2x1bWVzL0RBVEEvREFUQS9QdWNrL2JpZ3dpZy8vIgpCV3NfUmxvb3AgPC0gcGFzdGUwKGJ3X2RpcixsaXN0LmZpbGVzKGJ3X2RpcixwYXR0ZXJuPSJSbG9vcF8uK19jb21iaW5lZC5idyIpKQpwbG90X3Byb2ZpbGVfaGlfZ2VuZXNfUmxvb3BfREtPIDwtIHBsb3RfYndfcHJvZmlsZShid2ZpbGVzID0gQldzX1Jsb29wLGxvY2kgPSAiLi4vZ2Vub21lL2dlbmVzX2hpX2x0MTBrYi5tbTM5LmJlZCIsIG1vZGU9InN0cmV0Y2giLCBsYWJlbHMgPSBjbGVhbihCV3NfUmxvb3ApLHNob3dfZXJyb3I9VCxjb2xvcnMgPSBteXBhbCwgdmVyYm9zZT1GLCByZW1vdmVfdG9wPTAuMDAxKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygwLDI1KSkKcGxvdF9wcm9maWxlX2hpX2dlbmVzX1Jsb29wX0RLTyAKYGBgCmBgYHtyIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsZWNobz1GLHByb21wdD1GLHdhcm5pbmc9Rn0KcGxvdF9id19sb2NpX3N1bW1hcnlfaGVhdG1hcChCV3NfUmxvb3AsbG9jaSA9ICIuLi9nZW5vbWUvQ2hyb21ITU0xNy5jaHI5Lm1tMzlsaWZ0LmJlZCIsIHJlbW92ZV90b3AgPSAwLjAxKQpgYGAKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RixlY2hvPUYscHJvbXB0PUYsd2FybmluZz1GfQpwbG90X2J3X2xvY2lfc3VtbWFyeV9oZWF0bWFwKEJXcyxsb2NpID0gIi4uL2dlbm9tZS9DaHJvbUhNTTE3LmNocjkubW0zOWxpZnQuYmVkIiwgcmVtb3ZlX3RvcCA9IDAuMDEpCmBgYAoKYGBge3IgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RixlY2hvPUYscHJvbXB0PUYsd2FybmluZz1GfQpid19kaXIgPC0gIi9Wb2x1bWVzL0RBVEEvREFUQS9QdWNrL2JpZ3dpZy8vIgpCV3NfUmxvb3AgPC0gcGFzdGUwKGJ3X2RpcixsaXN0LmZpbGVzKGJ3X2RpcixwYXR0ZXJuPSJSbG9vcF8uK19jb21iaW5lZC5idyIpKQpwbG90X3Byb2ZpbGVfbG9fZ2VuZXNfUmxvb3BfREtPIDwtIHBsb3RfYndfcHJvZmlsZShid2ZpbGVzID0gQldzX1Jsb29wLGxvY2kgPSAiLi4vZ2Vub21lL2dlbmVzX2xvX2x0MTBrYi5tbTM5LmJlZCIsIG1vZGU9InN0cmV0Y2giLCBsYWJlbHMgPSBjbGVhbihCV3NfUmxvb3ApLHNob3dfZXJyb3I9VCxjb2xvcnMgPSBteXBhbCwgdmVyYm9zZT1GLCByZW1vdmVfdG9wPTAuMDAxKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygwLDI1KSkKcGxvdF9wcm9maWxlX2xvX2dlbmVzX1Jsb29wX0RLTyAKYGBgCmBgYHtyfQpCV3NfUmxvb3AgPC0gcGFzdGUwKGJ3X2RpcixsaXN0LmZpbGVzKGJ3X2RpcixwYXR0ZXJuPSJSbG9vcF8uK19SLi5idyIpKQpnZW5jb2RlIDwtICIuLi9nZW5vbWUvZ2VuY29kZS52bTMzLm1tMzkuYmVkIgpnZW5jb2RlIDwtICIuLi9nZW5vbWUvZ2VuZXNfbG9fbWlkX2hpX2JpLm1tMzkuYmVkIgoKQldzLldUIDwtIEJXc19SbG9vcFtncmVwKCJXVCIsQldzX1Jsb29wKV0KQldzLkZBTkNKIDwtIEJXc19SbG9vcFtncmVwKCJEMUQ2IixCV3NfUmxvb3ApXQpCV3MuREhYMzYgPC0gQldzX1Jsb29wW2dyZXAoIlAyRDIiLEJXc19SbG9vcCldCkJXcy5ES08gPC0gQldzX1Jsb29wW2dyZXAoIlAzRDQiLEJXc19SbG9vcCldCiAgCiMgQ2FsY3VsYXRlIGhlcmUgc29tZSBsb2NpIG9yIGJpbnMKY292LldUIDwtIGJ3X2xvY2koQldzLldULCBsb2NpID0gZ2VuY29kZSkKY292LkRIWDM2IDwtIGJ3X2xvY2koQldzLkRIWDM2LCBsb2NpID0gZ2VuY29kZSkKY292LkZBTkNKIDwtIGJ3X2xvY2koQldzLkZBTkNKLCBsb2NpID0gZ2VuY29kZSkKY292LkRLTyA8LSBid19sb2NpKEJXcy5ES08sIGxvY2kgPSBnZW5jb2RlKQpgYGAKCmBgYHtyfQpkaWZmX0RIWDM2IDwtIGJ3X2dyYW5nZXNfZGlmZl9hbmFseXNpcyhjb3YuV1QsIGNvdi5ESFgzNiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXVCIsICJESFgzNktPIikKZGlmZl9GQU5DSiA8LSBid19ncmFuZ2VzX2RpZmZfYW5hbHlzaXMoY292LldULCBjb3YuRkFOQ0osCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV1QiLCAiRkFOQ0oiKQpkaWZmX0RLTyA8LSBid19ncmFuZ2VzX2RpZmZfYW5hbHlzaXMoY292LldULCBjb3YuREtPLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldUIiwgIkRLTyIpCgoKIyBUaGlzIHRha2VzIGNhcmUgb2YgbG93IGNvbnRzIGFuZCB0aGluZ3MgbGlrZSB0aGlzLCBidXQgeW91IGNhbiBhbHNvIHVzZQojIGRpZmZfcmVzdWx0cyBhcyBpcyBmb3IgdGhlIHRoaW5ncyBiZWxvdwpsZmNfREhYMzYgPC0gREVTZXEyOjpsZmNTaHJpbmsoZGlmZl9ESFgzNiwgY29lZiA9ICJjb25kaXRpb25fV1RfdnNfREhYMzZLTyIsIHR5cGU9ImFwZWdsbSIpCmxmY19GQU5DSiA8LSBERVNlcTI6OmxmY1NocmluayhkaWZmX0ZBTkNKLCBjb2VmID0gImNvbmRpdGlvbl9XVF92c19GQU5DSiIsIHR5cGU9ImFwZWdsbSIpCmxmY19ES08gPC0gREVTZXEyOjpsZmNTaHJpbmsoZGlmZl9ES08sIGNvZWYgPSAiY29uZGl0aW9uX1dUX3ZzX0RLTyIsIHR5cGU9ImFwZWdsbSIpCgpkYXRhX0RIWDM2IDwtIHBsb3RNQShsZmNfREhYMzYsIHJldHVybkRhdGEgPSBUKQpkYXRhX0RIWDM2JGxmYyA8LSAtZGF0YV9ESFgzNiRsZmMKZGF0YV9ESFgzNiRtZWFuIDwtIGxvZzEwKGRhdGFfREhYMzYkbWVhbikKCmRhdGFfRkFOQ0ogPC0gcGxvdE1BKGxmY19GQU5DSiwgcmV0dXJuRGF0YSA9IFQpCmRhdGFfRkFOQ0okbGZjIDwtIC1kYXRhX0ZBTkNKJGxmYwpkYXRhX0ZBTkNKJG1lYW4gPC0gbG9nMTAoZGF0YV9GQU5DSiRtZWFuKQoKZGF0YV9ES08gPC0gcGxvdE1BKGxmY19ES08sIHJldHVybkRhdGEgPSBUKQpkYXRhX0RLTyRsZmMgPC0gLWRhdGFfREtPJGxmYwpkYXRhX0RLTyRtZWFuIDwtIGxvZzEwKGRhdGFfREtPJG1lYW4pCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0yLCBmaWcuaGVpZ2h0PTJ9Cmdnc2NhdHRlcihkYXRhX0RIWDM2LHggPSJtZWFuIix5PSJsZmMiLGNvbG9yPSJpc0RFIixzaXplID0gMC44LCBhbHBoYT0wLjUsIHBhbGV0dGUgPSBjKCJncmF5IixteXBhbFsxXSkpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MC4xKSArIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoLTUsNSkpCnBsb3RfTUFfREhYMzYgPC0gcmFzdGVyaXplKGxhc3RfcGxvdCgpLCBsYXllcnM9J1BvaW50JywgZHBpPTYwMCkgCmBgYApgYGB7ciBmaWcud2lkdGg9MiwgZmlnLmhlaWdodD0yfQpkYXRhX0RIWDM2JGNvdi5XVCA8LSByb3dNZWFucyhhcy5kYXRhLmZyYW1lKGNvdi5XVClbLDY6N10pCmRhdGFfREhYMzYkY292LkRIWDM2IDwtIHJvd01lYW5zKGFzLmRhdGEuZnJhbWUoY292LkRIWDM2KVssNjo3XSkKZ2dzY2F0dGVyKGRhdGFfREhYMzYseCA9ImNvdi5XVCIseT0iY292LkRIWDM2Iixjb2xvcj0iaXNERSIsc2l6ZSA9IDAuOCwgYWxwaGE9MC41LCBwYWxldHRlID0gYygiZ3JheSIsbXlwYWxbMV0pKSArIHNjYWxlX3hfY29udGludW91cyh0cmFucz0ibG9nIixsaW1pdHMgPSBjKDAuNSw0MDApKSArIHNjYWxlX3lfY29udGludW91cyh0cmFucz0ibG9nIixsaW1pdHMgPSBjKDAuNSw0MDApKSArIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MC4xKQpwbG90X1hZX0RIWDM2IDwtIHJhc3Rlcml6ZShsYXN0X3Bsb3QoKSwgbGF5ZXJzPSdQb2ludCcsIGRwaT02MDApIApgYGAKYGBge3IgZmlnLndpZHRoPTIsIGZpZy5oZWlnaHQ9Mn0KZGF0YV9ESFgzNiRjb3YuV1QgPC0gcm93TWVhbnMoYXMuZGF0YS5mcmFtZShjb3YuV1QpWyw2OjddKQpkYXRhX0RIWDM2JGNvdi5ESFgzNiA8LSByb3dNZWFucyhhcy5kYXRhLmZyYW1lKGNvdi5ESFgzNilbLDY6N10pCmdnc2NhdHRlcihkYXRhX0RIWDM2LHggPSJjb3YuV1QiLHk9ImNvdi5ESFgzNiIsY29sb3I9ImlzREUiLHNpemUgPSAwLjgsIGFscGhhPTAuNSwgcGFsZXR0ZSA9IGMoImdyYXkiLG15cGFsWzFdKSkgKyBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTAsNTApKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xMCw1MCkpICsgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjEpCnBsb3RfWFlfREhYMzZfem9vbSA8LSByYXN0ZXJpemUobGFzdF9wbG90KCksIGxheWVycz0nUG9pbnQnLCBkcGk9NjAwKSAKYGBgCgpgYGB7ciBmaWcud2lkdGg9MiwgZmlnLmhlaWdodD0yfQpnZ3NjYXR0ZXIoZGF0YV9GQU5DSix4ID0ibWVhbiIseT0ibGZjIixjb2xvcj0iaXNERSIsc2l6ZSA9IDAuOCwgYWxwaGE9MC41LCBwYWxldHRlID0gYygiZ3JheSIsbXlwYWxbMl0pKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKC01LDUpKQpwbG90X01BX0ZBTkNKIDwtIHJhc3Rlcml6ZShsYXN0X3Bsb3QoKSwgbGF5ZXJzPSdQb2ludCcsIGRwaT02MDApIApgYGAKCmBgYHtyIGZpZy53aWR0aD0yLCBmaWcuaGVpZ2h0PTJ9CmRhdGFfRkFOQ0okY292LldUIDwtIHJvd01lYW5zKGFzLmRhdGEuZnJhbWUoY292LldUKVssNjo3XSkKZGF0YV9GQU5DSiRjb3YuRkFOQ0ogPC0gcm93TWVhbnMoYXMuZGF0YS5mcmFtZShjb3YuRkFOQ0opWyw2OjddKQpnZ3NjYXR0ZXIoZGF0YV9GQU5DSix4ID0iY292LldUIix5PSJjb3YuRkFOQ0oiLGNvbG9yPSJpc0RFIixzaXplID0gMC44LCBhbHBoYT0wLjUsIHBhbGV0dGUgPSBjKCJncmF5IixteXBhbFsyXSkpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MC4xKSArIHNjYWxlX3hfY29udGludW91cyh0cmFucz0ibG9nIixsaW1pdHMgPSBjKDAuNSw0MDApKSArIHNjYWxlX3lfY29udGludW91cyh0cmFucz0ibG9nIixsaW1pdHMgPSBjKDAuNSw0MDApKSArIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MC4xKQpwbG90X1hZX0ZBTkNKIDwtIHJhc3Rlcml6ZShsYXN0X3Bsb3QoKSwgbGF5ZXJzPSdQb2ludCcsIGRwaT02MDApIApgYGAKCmBgYHtyIGZpZy53aWR0aD0yLCBmaWcuaGVpZ2h0PTJ9Cmdnc2NhdHRlcihkYXRhX0RLTyx4ID0ibWVhbiIseT0ibGZjIixjb2xvcj0iaXNERSIsc2l6ZSA9IDAuOCwgYWxwaGE9MC41LCBwYWxldHRlID0gYygiZ3JheSIsbXlwYWxbM10pKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKC01LDUpKQpwbG90X01BX0RLTyA8LSByYXN0ZXJpemUobGFzdF9wbG90KCksIGxheWVycz0nUG9pbnQnLCBkcGk9NjAwKSAKYGBgCgpgYGB7ciBmaWcud2lkdGg9MiwgZmlnLmhlaWdodD0yfQpkYXRhX0RLTyRjb3YuV1QgPC0gcm93TWVhbnMoYXMuZGF0YS5mcmFtZShjb3YuV1QpWyw2OjddKQpkYXRhX0RLTyRjb3YuREtPIDwtIHJvd01lYW5zKGFzLmRhdGEuZnJhbWUoY292LkRLTylbLDY6N10pCmdnc2NhdHRlcihkYXRhX0RLTyx4ID0iY292LldUIix5PSJjb3YuREtPIixjb2xvcj0iaXNERSIsc2l6ZSA9IDAuOCwgYWxwaGE9MC41LCBwYWxldHRlID0gYygiZ3JheSIsbXlwYWxbM10pKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkgKyBzY2FsZV94X2NvbnRpbnVvdXModHJhbnM9ImxvZyIsbGltaXRzID0gYygwLjUsNDAwKSkgKyBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9ImxvZyIsbGltaXRzID0gYygwLjUsNDAwKSkgKyBnZW9tX2FibGluZShzbG9wZSA9IDEsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkKcGxvdF9YWV9ES08gPC0gcmFzdGVyaXplKGxhc3RfcGxvdCgpLCBsYXllcnM9J1BvaW50JywgZHBpPTYwMCkgCmBgYAoKYGBge3IgZmlnLndpZHRoPTIsIGZpZy5oZWlnaHQ9Mn0KZGF0YV9ES08kbGZjX0RIWDM2IDwtIGRhdGFfREhYMzYkbGZjCmRhdGFfREtPJGxmY19GQU5DSiA8LSBkYXRhX0ZBTkNKJGxmYwpnZ3NjYXR0ZXIoZGF0YV9ES08seCA9ImxmY19ESFgzNiIseT0ibGZjIixjb2xvcj0iaXNERSIsc2l6ZSA9IDAuOCwgYWxwaGE9MC41LCBwYWxldHRlID0gYygiZ3JheSIsbXlwYWxbM10pKSsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MC4xKSArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKC01LDUpLCB4bGltPWMoLTUsNSkpCnBsb3RfTEZDX0RLT192c19ESFgzNiA8LSByYXN0ZXJpemUobGFzdF9wbG90KCksIGxheWVycz0nUG9pbnQnLCBkcGk9NjAwKQpgYGAKYGBge3IgZmlnLndpZHRoPTIsIGZpZy5oZWlnaHQ9Mn0KZ2dzY2F0dGVyKGRhdGFfREtPLHggPSJsZmNfRkFOQ0oiLHk9ImxmYyIsY29sb3I9ImlzREUiLHNpemUgPSAwLjgsIGFscGhhPTAuNSwgcGFsZXR0ZSA9IGMoImdyYXkiLG15cGFsWzNdKSkrIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjEpICsgY29vcmRfY2FydGVzaWFuKHlsaW09YygtNSw1KSwgeGxpbT1jKC01LDUpKQpwbG90X0xGQ19ES09fdnNfRkFOQ0ogPC0gcmFzdGVyaXplKGxhc3RfcGxvdCgpLCBsYXllcnM9J1BvaW50JywgZHBpPTYwMCkKYGBgCmBgYHtyIGZpZy53aWR0aD0yLCBmaWcuaGVpZ2h0PTJ9CmRhdGFfREhYMzYkbGZjX0RLTyA8LSBkYXRhX0RLTyRsZmMKZ2dzY2F0dGVyKGRhdGFfREhYMzYseCA9ImxmYyIseT0ibGZjX0RLTyIsY29sb3I9ImlzREUiLHNpemUgPSAwLjgsIGFscGhhPTAuNSwgcGFsZXR0ZSA9IGMoImdyYXkiLG15cGFsWzFdKSkrIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTAuMSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjEpICsgY29vcmRfY2FydGVzaWFuKHlsaW09YygtNSw1KSwgeGxpbT1jKC01LDUpKQpgYGAKCgpgYGB7cn0KY292IDwtIGNiaW5kKCBhcy5kYXRhLmZyYW1lKGNvdi5XVClbLDE6N10sIAogICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoY292LkRIWDM2KVssNjo3XSwgCiAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShjb3YuRkFOQ0opWyw2OjddLCAKICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKGNvdi5ES08pWyw2OjddKQoKY29sbmFtZXMoY292KSA8LSBjKGNvbG5hbWVzKGNvdilbMTo1XSwiV1QxIiwiV1QyIiwiREhYMSIsIkRIWDIiLCJGQU4xIiwiRkFOMiIsIkRLTzEiLCJES08yIikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0zfQpsaWJyYXJ5KHJlc2hhcGUyKQptZGYgPC0gbWVsdChkYXRhLmZyYW1lKG5hbWU9Y292LldUJG5hbWUsY292Wyw2OjEzXSkpCm1kZiA8LSBtZGZbbWRmJHZhbHVlPDUwMCxdCm1kZiRjb25kIDwtICJXVCIKbWRmJGNvbmRbZ3JlcCgiREhYIixtZGYkdmFyaWFibGUpXSA8LSAiREhYMzYtLy0iCm1kZiRjb25kW2dyZXAoIkZBTiIsbWRmJHZhcmlhYmxlKV0gPC0gIkZBTkNKLS8tIgptZGYkY29uZFtncmVwKCJES08iLG1kZiR2YXJpYWJsZSldIDwtICJESFgzNi0vLUZBTkNKLS8tIgpwbG90X3Zpb2xfcmVwX2FsbF9wZWFrcyA8LSBnZ3Zpb2xpbihtZGYsIHg9InZhcmlhYmxlIix5PSJ2YWx1ZSIsZmlsbD0iY29uZCIscGFsZXR0ZSA9IG15cGFsLCBhZGQ9Im1lYW5fc2QiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDEwKSkKcGxvdF92aW9sX3JlcF9hbGxfcGVha3MKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0zfQpwbG90X3Zpb2xfcmVwX2hpX2dlbmVzIDwtIGdndmlvbGluKG1kZlttZGYkbmFtZT09ImdlbmVfaGkiLF0sIHg9InZhcmlhYmxlIix5PSJ2YWx1ZSIsZmlsbD0iY29uZCIscGFsZXR0ZSA9IG15cGFsLCBhZGQ9Im1lYW5fc2QiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDEwKSkKcGxvdF92aW9sX3JlcF9oaV9nZW5lcwpgYGAKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9M30KcGxvdF92aW9sX3JlcF9iaXZfZ2VuZXMgPC0gZ2d2aW9saW4obWRmW21kZiRuYW1lPT0iZ2VuZV9iaSIsXSwgeD0idmFyaWFibGUiLHk9InZhbHVlIixmaWxsPSJjb25kIixwYWxldHRlID0gbXlwYWwsIGFkZD0ibWVhbl9zZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsMTApKQpwbG90X3Zpb2xfcmVwX2Jpdl9nZW5lcwpgYGAKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9M30KcGxvdF92aW9sX3JlcF9ieV9nZW5lcyA8LSBnZ3Zpb2xpbihtZGYsIHg9Im5hbWUiLHk9InZhbHVlIixmaWxsPSJjb25kIixwYWxldHRlID0gbXlwYWwsIGFkZD0ibWVhbl9zZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsMTApKQpwbG90X3Zpb2xfcmVwX2J5X2dlbmVzCmBgYAo=